package com.lightsaber.trade.core.service.account.impl;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.lightsaber.trade.core.common.util.OSCacheUtil;
import com.lightsaber.trade.core.dao.account.MenuDao;
import com.lightsaber.trade.core.entity.account.Menu;
import com.lightsaber.trade.core.entity.account.nopersist.HomeMenuBar;
import com.lightsaber.trade.core.entity.account.nopersist.MenuBar;
import com.lightsaber.trade.core.entity.account.nopersist.MenuItem;
import com.lightsaber.trade.core.service.account.MenuService;

/**
 * @author Ming
 * 
 */
@Component
@Transactional(readOnly = true)
public class MenuServiceImpl implements MenuService {

    private final static Logger LOG = LoggerFactory.getLogger(MenuServiceImpl.class);

    @Autowired
    private MenuDao menuDao;

    private String byteToString(final Byte theByte) {
        if (theByte == null) {
            return "";
        }

        return theByte.toString();
    }

    @Override
    public MenuBar generateMenuByRoleCode(final String userCode, final String roleCode,
            String language) {

        List<Menu> listMenu = getMenuList(userCode, roleCode);

        // convert to menu item
        Map<String, MenuItem> menuMap = new HashMap<String, MenuItem>();
        for (Menu menu : listMenu) {
            String level1Pos = byteToString(menu.getLevel1Pos());
            String level2Pos = byteToString(menu.getLevel2Pos());
            String level3Pos = byteToString(menu.getLevel3Pos());
            String level4Pos = byteToString(menu.getLevel4Pos());

            String munuPosKey = level1Pos + level2Pos + level3Pos + level4Pos;
            String sequence = StringUtils.substring(munuPosKey, munuPosKey.length() - 1);

            // Get menu name by local language
            String menuName = getMenuNameByLocalLanguage(language, menu);

            MenuItem menuItem = new MenuItem(menuName, menu.getFuncAction(), menu.getFuncCode(),
                    Integer.valueOf(sequence));
            menuMap.put(munuPosKey, menuItem);
        }

        // 3. generate menu bar
        MenuBar menuBar = new MenuBar();

        Set<Map.Entry<String, MenuItem>> entrySet = menuMap.entrySet();
        for (Map.Entry<String, MenuItem> entry : entrySet) {

            String munuPosKey = entry.getKey();
            MenuItem menuItem = entry.getValue();

            if (munuPosKey.length() == 1) {// is the top level
                menuBar.putTopLevelMenuItem(menuItem);
            } else {// is leaf node
                String parentKey = StringUtils.substring(munuPosKey, 0, munuPosKey.length() - 1);
                MenuItem parent = menuMap.get(parentKey);
                if (parent == null) {
                    LOG.error("The leaf node {} has not parent menu node!", munuPosKey);
                } else {
                    parent.putMenuItem(menuItem);
                }
            }
        }

        return menuBar;
    }

    @Override
    public HomeMenuBar generateHomeMenuByRoleCode(final String userCode, final String roleCode,
            String language) {

        List<Menu> listMenu = getMenuList(userCode, roleCode);

        // convert to menu item
        Map<String, MenuItem> menuMap = new HashMap<String, MenuItem>();
        for (Menu menu : listMenu) {
            String level1Pos = byteToString(menu.getLevel1Pos());
            String level2Pos = byteToString(menu.getLevel2Pos());
            String level3Pos = byteToString(menu.getLevel3Pos());
            String level4Pos = byteToString(menu.getLevel4Pos());

            String munuPosKey = level1Pos + level2Pos + level3Pos + level4Pos;
            LOG.info("munuPosKey : {}", munuPosKey);
            String sequence = StringUtils.substring(munuPosKey, munuPosKey.length() - 1);
            LOG.info("sequence : {}", sequence);
            MenuItem menuItem = null;

            // Get menu name by local language
            String menuName = getMenuNameByLocalLanguage(language, menu);

            if (munuPosKey.length() == 1) {
                menuItem = new MenuItem(menuName, menu.getFuncAction(), menu.getFuncCode(),
                        Integer.valueOf(sequence), Integer.valueOf(munuPosKey));
            } else {
                menuItem = new MenuItem(menuName, menu.getFuncAction(), menu.getFuncCode(),
                        Integer.valueOf(sequence));
            }
            menuMap.put(munuPosKey, menuItem);
        }

        // generate menu bar
        HomeMenuBar homeMenuBar = new HomeMenuBar();

        Set<Map.Entry<String, MenuItem>> entrySet = menuMap.entrySet();
        for (Map.Entry<String, MenuItem> entry : entrySet) {

            String munuPosKey = entry.getKey();
            MenuItem menuItem = entry.getValue();

            if (munuPosKey.length() == 1) {// is the top level
                homeMenuBar.putTopLevelMenuItem(menuItem);
            } else {// is leaf node
                String parentKey = StringUtils.substring(munuPosKey, 0, munuPosKey.length() - 1);
                MenuItem parent = menuMap.get(parentKey);
                if (parent == null) {
                    LOG.error("The leaf node {} has not parent menu node!", munuPosKey);
                } else {
                    parent.putMenuItem(menuItem);
                }
            }
        }

        return homeMenuBar;
    }

    @SuppressWarnings("unchecked")
    private List<Menu> getMenuList(final String userCode, final String roleCode) {

        String cacheGroup = "menu";
        String key = "MENULIST_" + userCode + "_" + roleCode;
        LOG.debug("cache key: {}", key);

        Callable<List<Menu>> function = new Callable<List<Menu>>() {

            @Override
            public List<Menu> call() throws Exception {

                List<Menu> listMenu = menuDao.findByFuncCode(roleCode, userCode);
                checkParentMenu(listMenu);
                return listMenu;
            }
        };

        int refreshPeriod = -1; // never refresh

        try {
            return OSCacheUtil.cache(key, function, cacheGroup, refreshPeriod);
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
        }

        return Collections.EMPTY_LIST;
    }

    // // // changed by 2011-08-02
    private void checkParentMenu(final List<Menu> listMenu) {

        for (Menu menu : listMenu) {
            Byte level1Pos = menu.getLevel1Pos();
            Byte level2Pos = menu.getLevel2Pos();
            Byte level3Pos = menu.getLevel3Pos();
            Byte level4Pos = menu.getLevel4Pos();

            if (level4Pos != null) {
                Menu level3Menu = menuDao
                        .findByLevelPosition(level1Pos, level2Pos, level3Pos, null);
                Menu level2Menu = menuDao.findByLevelPosition(level1Pos, level2Pos, null, null);
                Menu level1Menu = menuDao.findByLevelPosition(level1Pos, null, null, null);
                if (level3Menu == null || level2Menu == null || level1Menu == null) {
                    listMenu.remove(menu);
                }
                continue;
            }
            if (level3Pos != null) {
                Menu level2Menu = menuDao.findByLevelPosition(level1Pos, level2Pos, null, null);
                Menu level1Menu = menuDao.findByLevelPosition(level1Pos, null, null, null);
                if (level2Menu == null || level1Menu == null) {
                    listMenu.remove(menu);
                }
                continue;
            }
            if (level2Pos != null) {
                Menu level1Menu = menuDao.findByLevelPosition(level1Pos, null, null, null);
                if (level1Menu == null) {
                    listMenu.remove(menu);
                }
                continue;
            }

        }
    }

    private String getMenuNameByLocalLanguage(String language, Menu menu) {
        String menuName;

        if (StringUtils.equals("zh_CN", language)) {
            menuName = menu.getMenuCnZhName();
        } else {
            menuName = menu.getMenuName();
        }
        return menuName;
    }

}
